﻿using System;
using System.Collections;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.Security.Principal;
using ActiveDirectoryUtilities.DirectoryServiceAccess;
using Microsoft.SqlServer.Server;
using System.DirectoryServices.Protocols;
using AccessActiveDirectory;

public partial class UserDefinedFunctions
{
	[SqlFunction(FillRowMethodName = "FillDomains", DataAccess = DataAccessKind.Read
		, TableDefinition = 
		"DnsDomainName NVARCHAR(MAX)"
		+ ", NetBiosDomainName NVARCHAR(MAX)"
		+ ", NcName NVARCHAR(MAX)"
		+ ", DomainMode NVARCHAR(MAX)"
		+ ", IsNtMixedDomain bit"
		+ ", TrustParent NVARCHAR(MAX)"
		+ ", CrossRefDistinguishedName NVARCHAR(MAX)"
		)]
	public static IEnumerable fn_GetDomains(SqlString fqdnDomainName)
	{
		EventLogTraceListener eltl = new EventLogTraceListener("Application");
		if (ConfiguredSettings.TraceLevel >= 4)
		{
			Trace.Listeners.Add(eltl);
		}

		WindowsImpersonationContext executionContext = null;
		try
		{
			WindowsIdentity callerId = SqlContext.WindowsIdentity;
			if (callerId != null)
			{
				executionContext = callerId.Impersonate();

				string domainName = string.Empty;
				if (!fqdnDomainName.IsNull)
				{
					domainName = (string)fqdnDomainName;
				}

				Forest f = null;
				try
				{
					if (!string.IsNullOrEmpty(domainName))
					{
						f = Forest.GetForest((string)fqdnDomainName);
					}
					else
					{
						f = Forest.GetCurrentForest();
					}

					return f.Domains;
				}
				catch (LdapException ex)
				{
					if (ex.ErrorCode == 81)
					{
						throw new ApplicationException("Couldn't contact a DC for the forest root.  Ensure DNS 'A' records for the forest root domain are properly registered.");
					}
					else
					{
						SharedLogic.LogTraceInfo(eltl, ex, TraceLevel.Error);
						throw ex;
					}
				}
				catch (Exception ex)
				{
					SharedLogic.LogTraceInfo(eltl, ex, TraceLevel.Error);
					throw ex;
				}

				throw new ApplicationException("Invalid codepath traversed.  Caller should never make it here.");
			}
		}
		catch (Exception ex)
		{
			if (!Trace.Listeners.Contains(eltl))
			{
				Trace.Listeners.Add(eltl);
			}
			Trace.TraceError("Exception Type:\t{0}\n\rError Message:\t{1}\n\rStack:\t{2}", ex.GetType().ToString(), ex.Message, ex.StackTrace);
			//Consider removing eltl if it was added in this module
			throw ex;
		}
		finally
		{
			if (Trace.Listeners.Contains(eltl))
			{
				Trace.Listeners.Remove(eltl);
			}
			if (executionContext != null)
			{
				executionContext.Undo();
			}
		}
		return null;
	}

	private static void FillDomains(
		object o
		, out SqlString dnsDomainName
		, out SqlString netBiosDomainName
		, out SqlString nCName
		, out SqlString domainMode
		, out SqlBoolean isNtMixedDomain
		, out SqlString trustParent
		, out SqlString distinguishedName
		)
	{
		Domain d = (Domain)o;
		dnsDomainName = d.DnsDomainName;
		netBiosDomainName = d.NetBiosName;
		distinguishedName = d.CrossRefDN;
		domainMode = d.MsDsBehaviorVersion.ToString().Replace('_', ' ');
		nCName = d.NcName;
		isNtMixedDomain = d.IsNtMixedDomain;
		trustParent = d.TrustParent;
	}
};

